package com.ysxn.communicate.socket;

import com.jtcp.CommFrame;
import com.jtcp.MainFrame;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;

import com.ysxn.communicate.CommClientInterface;

import com.ysxn.util.GTools;
import com.ysxn.util.HexTool;
import com.ysxn.util.TLogger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 通信接口客户端的socket实现 主站监听两个端口，das和terminal
 * 1.das发起的命令（如下发等命令），由das建立seq，主站在终端对象中记录这个seq，用这个seq%16做为新的seq发给terminal，
 * terminal回复后也根据这个seq来查找das命令，进行回复
 * 2.terminal发起的命令（如心跳），由终端建立seq，CommClientImpl直接根据命令中的seq回复terminal
 * 3.由定时任务发起得命令（如定时抄二类数据），由terminal建立seq，发给终端
 *
 * 因为修改了算法，只有定时任务得命令才入定时器，下面的描述作废----------------------------
 * 向terminal发送需要回复的命令后，设置终端忙标志，收到终端回复后，设置终端闲
 * 向terminal发送命令时，如果终端忙，并且当前时间-lastSendTime小于30000，则放入定时器 如果是给终端发确认帧，则不等待，直接发
 * ----
 * --------------------------------------------------------------------------
 *
 * @author stronghill
 *
 */
public class CommClientImpl implements CommClientInterface {

    private final static Log log = (Log) LogFactory.getLog(CommClientImpl.class);
    private MainFrame mf = null;
    private CommFrame cf = null;

    public Timer getCtimer() {
        if (null == ctimer) {
            ctimer = new Timer();
        }
        return ctimer;
    }
    /**
     * 上次收到数据的时间
     */
    private long lastRecvTime = System.currentTimeMillis();
    /**
     * 已加载的定时任务的最后结束时间
     */
    private long lastScheduleEndTime = 0;

    public void setCtimer(Timer ctimer) {
        this.ctimer = ctimer;
    }

    private Socket client = null;
    private OutputStream out;
    private boolean isConnect = false;
    private SocketClientThread cthread = null;

    /**
     * 关联的终端id
     */
    private String idTerminal = null;
    /**
     * 关联的日志路径
     */
    private String tpath = null;
    /**
     * client类型，D_M,das到主站的连接 ； T_M,终端到主站的连接
     */
    private String ctype = null;

    /**
     * 定时发送命令的定时器
     */
    private Timer ctimer = null;
    /**
     * 上次发送时间，用于控制发送频率，不能连续给终端发命令
     */
    private long lastSendTime = 0;
    /**
     * 终端忙标志
     */
    private boolean tbusy = false;

    public boolean isTbusy() {
        return tbusy;
    }

    public void setTbusy(boolean tbusy) {
        this.tbusy = tbusy;
    }

    public int clearTimer() {
        ctimer.cancel();
        ctimer = null;
        log.debug("clearTimer");
        return 0;
    }

//	private int sendCommand() {
//		return 0;
//	}
    public String getTpath() {
        return tpath;
    }

    public void setTpath(String tpath) {
        this.tpath = tpath;
    }

    /**
     * 客户端实现，异步调用通用的发送方法,将命令放入命令池，定时发送
     *
     * @param data 要发送的数据
     * @param off 要发送的数据起始字节,从0开始
     * @param len 要发送的数据的长度
     * @return 发送byte串长度，小于0表示失败
     */
    @Override
    public synchronized int send(byte[] data, int off, int len) {
        if (null == client) {
            return -1;
        } else {
        }
        try {
            out = client.getOutputStream();
            out.write(data, off, len);
            out.flush();
            String strdata = HexTool.byteToHex(data, " ");
            TLogger.getInstance().log(getTpath(), getIdTerminal(),
                    getClient().toString() + getClient().hashCode() + "_send",
                    strdata);
            return data.length;
        } catch (Exception e) {
            log.error(
                    "send error", e);
            return -2;
        } finally {

        }
    }

    public long getLastSendTime() {
        return lastSendTime;
    }

    public void setLastSendTime(long lastSendTime) {
        this.lastSendTime = lastSendTime;
    }

    public String getIdTerminal() {
        return idTerminal;
    }

    public void setIdTerminal(String idTerminal) {
        this.idTerminal = idTerminal;
    }

    /**
     * 处理接收数据的回调函数
     *
     * @param data 收到的数据
     * @param count 收到的数据的长度
     * @return
     */
    @Override
    public int onReceived(byte[] data, int count) {
        if (count < 1) {
            return count;
        } else {

        }
        byte[] rdata = null;
        try {

            String breceivedString = "";
            lastRecvTime = System.currentTimeMillis();
            rdata = new byte[count];
            for (int i = 0; i < count; i++) {
                rdata[i] = data[i];
            }

            String back16srt = HexTool.byteToHex(rdata, " ");
            // Terminal thisTerminal =
            log.info("received：" + back16srt);
            cf.onReceived(rdata , count);

            tpath = TLogger.PATH_PHONE;
            TLogger.getInstance()
                    .log(tpath, idTerminal,
                            client.toString() + client.hashCode()
                            + ",recv",
                            HexTool.byteToHex(rdata, " "));

        } catch (Exception e) {
            log.error("recvData, idTerminal=" + idTerminal + ",data=" + HexTool.byteToHex(rdata, " "),
                    e);
        }

        return 0;

    }

    public int connect(String ip, int port) {

        try {
            client = new Socket(ip, port);
            // setOut(client.getOutputStream());
            this.setIdTerminal("client");
            out = client.getOutputStream();
            cthread = new SocketClientThread(this);
            cthread.start();
            isConnect = true;
        } catch (UnknownHostException e) {
            log.error(
                    "UnknownHost:ip=" + ip + ",port=" + port, e);
            return -1;
        } catch (Exception e) {
            log.error(
                    "connect error:ip=" + ip + ",port=" + port, e);
            return -2;
        }

        return 0;
    }

    public boolean isConnected() {
        return isConnect;
    }

    public Socket getClient() {
        return client;
    }

    public void setClient(Socket client) {
        isConnect = true;
        this.client = client;
    }

    public int close() {
        try {
            if (null != client) {
                client.close();
                isConnect = false;
                client = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return -1;
        }

        return 0;
    }

    public byte[] sendSync(byte[] data, int off, int len) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @return the out
     */
    public OutputStream getOut() {
        return out;
    }

    public void setOut(OutputStream out) {
        this.out = out;
    }

    /**
     * 检查上次收到数据时间，如果大于指定分钟没有收到数据，就关闭连接
     *
     * @return 0，未超时；1，超时，并已关闭；其他，关闭失败
     */
    public int checkLastRecvTime() {

        if ((System.currentTimeMillis() - lastRecvTime) > (CommClientInterface.CHECKOFFMIN * 60 * 1000)) {
            try {
                log.debug("检查超时终端" + idTerminal
                        + ",socket=" + this.getClient());
                close();
                return 1;
            } catch (Exception e) {
                log.error(this.getIdTerminal() + "连接超时关闭失败", e);
                return -1;
            }
        }
        return 0;
    }

    public long getLastScheduleEndTime() {
        return lastScheduleEndTime;
    }

    public void setLastScheduleEndTime(long lastScheduleEndTime) {
        this.lastScheduleEndTime = lastScheduleEndTime;
    }

    /**
     * @return the mf
     */
    public MainFrame getMf() {
        return mf;
    }

    /**
     * @param mf the mf to set
     */
    public void setMf(MainFrame mf) {
        this.mf = mf;
    }

    /**
     * @return the cf
     */
    public CommFrame getCf() {
        return cf;
    }

    /**
     * @param cf the cf to set
     */
    public void setCf(CommFrame cf) {
        this.cf = cf;
    }

}
